#include "vector.h"
#include <math.h>
#include "math.h"

#ifdef _DEBUG
#include "../engine/error.h"
#endif

const Vector3f EE_EXPORT Vector3f::FORWARD = Vector3f(0.0f,0.0f,1.0f); 
const Vector3f EE_EXPORT Vector3f::BACKWARD = Vector3f(0.0f,0.0f,-1.0f); 
const Vector3f EE_EXPORT Vector3f::LEFT = Vector3f(-1.0f,0.0f,0.0f); 
const Vector3f EE_EXPORT Vector3f::RIGHT = Vector3f(1.0f,0.0f,0.0f); 
const Vector3f EE_EXPORT Vector3f::UP = Vector3f(0.0f,1.0f,0.0f); 
const Vector3f EE_EXPORT Vector3f::DOWN = Vector3f(0.0f,-1.0f,0.0f); 

Vector2i Vector2i::operator += (const Vector2i &op)
{
	x += op.x; 
	y += op.y; 
	return *this; 
}
Vector2i Vector2i::operator -= (const Vector2i &op)
{
	x += op.x; 
	y += op.y; 
	return *this; 
}

void Vector2i::zero(Vector2i *v1)
{
#ifdef _DEBUG
	if(v1 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return; 
	}
#endif
	v1->x = 0; 
	v1->y = 0; 
}

Vector2f Vector2i::operator	* (const float &op) const
{
	return Vector2f(x * op, y * op); 
}

float Vector2i::length(void)
{
	const float dp = (float)(x*x + y*y); 
	return (dp == 0.0f) ? 0.0f : sqrtf(dp); 
}

Vector2f Vector2f::operator	* (const float &op) const
{
	return Vector2f(x * op, y * op); 
}

Vector2f Vector2f::operator	+ (const Vector2f &op) const
{
	return Vector2f(x + op.x, y + op.y);
}

Vector2f Vector2f::operator - (const Vector2f &op) const
{
	return Vector2f(x - op.x, y - op.y);
}

Vector2f Vector2f::operator+=(const Vector2f &op)
{
	x += op.x; 
	y += op.y; 
	return *this; 
}

Vector2f Vector2f::operator-=(const Vector2f &op)
{
	x += op.x; 
	y += op.y; 
	return *this; 
}

float Vector2f::length(void)
{
	const float dp = x * x + y * y; 
	return (dp == 0.0f) ? 0.0f : sqrtf(dp); 
}

void Vector2f::zero(Vector2f *v1)
{
#ifdef _DEBUG
	if(v1 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return; 
	}
#endif
	v1->x = 0.0f; 
	v1->y = 0.0f; 
}

float Vector3f::length(void)
{
	const float dp = x * x + y * y + z * z; 
	return (dp == 0.0f) ? dp : sqrtf(dp); 
}

void Vector3f::setLength(const float length)
{
	if(length == 0.0f)
		x = y = z = 0.0F; 
	else
	{

#ifdef _DEBUG
		if(Vector3f::isZero(this))
		{
			EE_LOGERROR("Invalid vector"); 
			return; 
		}
#endif
		const float len = sqrtf(x * x + y * y + z * z); 
		const float factor = length / len; 

		x *= factor; 
		y *= factor; 
		z *= factor; 
	}
}

bool Vector3f::isZero(const Vector3f *v1)
{
#ifdef _DEBUG
	if(v1 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return false; 
	}
#endif
	return (v1->x == 0.0f && v1->y == 0.0f && v1->z == 0.0f); 
}

void Vector3f::zero(Vector3f *v1)
{
#ifdef _DEBUG
	if(v1 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return; 
	}
#endif
	v1->x = 0.0f; 
	v1->y = 0.0f; 
	v1->z = 0.0f; 
}

Vector3f Vector3f::normalOf(const Vector3f *v1)
{
#ifdef _DEBUG
	if(v1 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return Vector3f(); 
	}
#endif

	const float x = v1->x; 
	const float y = v1->y; 
	const float z = v1->z; 
	const float len = sqrtf(x*x + y*y + z*z); 

#ifdef _DEBUG
	if(len == NULL)
	{
		EE_LOGERROR("Divide by zero"); 
		return Vector3f(); 
	}
#endif
	return Vector3f(x / len, y / len, z / len); 
}


void Vector3f::maxVec(Vector3f* pOut, Vector3f* v1, Vector3f* v2)
{
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL || pOut == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return; 
	}
#endif
	pOut->x = MAX(v1->x, v2->x); 
	pOut->y = MAX(v1->y, v2->y); 
	pOut->z = MAX(v1->z, v2->z); 
}

void Vector3f::minVec(Vector3f* pOut, Vector3f* v1, Vector3f* v2)
{
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL || pOut == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return; 
	}
#endif
	pOut->x = MIN(v1->x, v2->x); 
	pOut->y = MIN(v1->y, v2->y); 
	pOut->z = MIN(v1->z, v2->z); 
}

float Vector3f::length(Vector3f *v)
{
#ifdef _DEBUG
	if(v == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return 0.0f; 
	}
#endif
		
	const float x = v->x; 
	const float y = v->y; 
	const float z = v->z;
	return sqrtf(x*x + y*y + z*z); 
}

void Vector3f::normalize(Vector3f *v1)
{
#ifdef _DEBUG
	if(v1 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return; 
	}
#endif

	const float x = v1->x; 
	const float y = v1->y; 
	const float z = v1->z; 
	const float len = sqrtf(x*x + y*y + z*z); 

#ifdef _DEBUG
	if(len == NULL)
	{
		EE_LOGERROR("Divide by zero"); 
		return; 
	}
#endif

	v1->x = x / len; 
	v1->y = y / len; 
	v1->z = z / len; 

}

void Vector3f::crossNormalize(Vector3f *vOut, const Vector3f *v1, const Vector3f *v2)
{
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL || vOut == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return; 
	}
#endif

	const float x = (v1->y * v2->z) - (v1->z * v2->y); 
	const float y = (v1->z * v2->x) - (v1->x * v2->z); 
	const float z = (v1->x * v2->y) - (v1->y * v2->x); 

	const float len = sqrtf((x*x) + (y*y) + (z*z)); 

#ifdef _DEBUG
	if(len == 0.0f)
	{
		EE_LOGERROR("Divide by zero"); 
		return; 
	}
#endif

	vOut->x = x / len; 
	vOut->y = y / len; 
	vOut->z = z / len; 
}

void Vector3f::cross(Vector3f *vOut, const Vector3f *v1, const Vector3f *v2)
{	
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL || vOut == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return; 
	}
#endif

	vOut->x = (v1->y * v2->z) - (v1->z * v2->y); 
	vOut->y = (v1->z * v2->x) - (v1->x * v2->z); 
	vOut->z = (v1->x * v2->y) - (v1->y * v2->x); 
}

Vector3f Vector3f::cross(const Vector3f *v1, const Vector3f *v2)
{
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return Vector3f(); 
	}
#endif

	return Vector3f(
		(v1->y * v2->z) - (v1->z * v2->y),
		(v1->z * v2->x) - (v1->x * v2->z),
		(v1->x * v2->y) - (v1->y * v2->x)); 
}

float Vector3f::distance(const Vector3f *v1, const Vector3f *v2)
{
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return 0.0f; 
	}
#endif

	const float dx = (v1->x - v2->x); 
	const float dy = (v1->y - v2->y); 
	const float dz = (v1->z - v2->z); 

	return sqrtf((dx * dx) + (dy * dy) + (dz * dz)); 
}

float Vector3f::distanceSquared(const Vector3f *v1, const Vector3f *v2)
{
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return 0.0f; 
	}
#endif

	const float dx = (v1->x - v2->x); 
	const float dy = (v1->y - v2->y); 
	const float dz = (v1->z - v2->z); 

	return (dx * dx) + (dy * dy) + (dz * dz); 
}
float Vector3f::dot(const Vector3f *v1)
{
#ifdef _DEBUG
	if(v1 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return 0.0f; 
	}
#endif
	return (v1->x * v1->x) + (v1->y * v1->y) + (v1->z * v1->z); 

}
float Vector3f::dot(const Vector3f *v1, const Vector3f *v2)
{
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return 0.0f; 
	}
#endif
	return (v1->x * v2->x) + (v1->y * v2->y) + (v1->z * v2->z); 
}

float Vector3f::angle(const Vector3f *v1, const Vector3f *v2)
{
#ifdef _DEBUG
	if(v1 == NULL || v2 == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		return 0.0f; 
	}
#endif
	const float L1 = (float) sqrtf(v1->x * v1->x + v1->y * v1->y + v1->z * v1->z); 
	if(L1 == 0.0f) return 0.0f; 
	const float L2 = (float) sqrtf(v2->x * v2->x + v2->y * v2->y + v2->z * v2->z); 
	if(L2 == 0.0f) return 0.0f; 

	return acosf(((v1->x / L1) * (v2->x / L2)) + ((v1->y / L1) * (v2->y / L2)) + ((v1->z / L1) * (v2->z) / L2)); 
}

Vector3f Vector3f::operator + (const Vector3f &op) const
{
	return Vector3f(x + op.x, y + op.y, z + op.z); 
}
Vector3f Vector3f::operator - (const Vector3f &op) const
{
	return Vector3f(x - op.x, y - op.y, z - op.z); 
}

Vector3f Vector3f::operator-=(const Vector3f &op)
{
	x -= op.x; 
	y -= op.y; 
	z -= op.z; 
	return *this; 
}

Vector3f Vector3f::operator+=(const Vector3f &op)
{
	x += op.x; 
	y += op.y; 
	z += op.z; 
	return *this; 
}

Vector3f Vector3f::operator/=(const float &op)
{
	x /= op; 
	y /= op; 
	z /= op; 
	return *this; 
}

Vector3f Vector3f::operator*=(const float &op)
{
	x *= op; 
	y *= op; 
	z *= op; 
	return *this; 
}

Vector3f Vector3f::operator / (const float &op) const
{
	return Vector3f(x / op, y / op, z / op); 
}
Vector3f Vector3f::operator * (const float &op) const
{
	return Vector3f(x * op, y * op, z * op); 
}
float Vector3f::operator	* (const Vector3f &op) const
{
	return((x * op.x) + (y * op.y) + (z * op.z)); 
}

Vector3f Vector3f::operator=(float *op)
{
#ifdef _DEBUG
	if(op == NULL)
	{
		EE_LOGERROR("Invalid parameter"); 
		x = y = z = 0.0f; 
		return Vector3f(); 
	}
#endif

	x = op[0]; 
	y = op[1]; 
	z = op[2]; 
	return *this; 
}

Vector3f::operator	float * ()
{
	return (float *) this; 
}
Vector3f::operator	float * () const
{
	return (float *) this; 
}
Vector3f Vector3f::operator + () const
{
	return *this; 
}
Vector3f Vector3f::operator - () const
{
	return Vector3f(-x, -y, -z); 
}

bool Vector3f::operator == (const Vector3f &op) const
{
	return(op.x == x && op.y == y && op.z == z); 
}

bool Vector3f::operator != (const Vector3f &op) const
{
	return !(op.x == x && op.y == y && op.z == z); 
}
